﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Web.Mvc;
using System.Text;
using System.Linq.Expressions;

using Mvc.XForms.Controls.Formatters;
using Mvc.XForms.Utilities;

namespace Mvc.XForms.Controls {
	public abstract class CoreControlBase<TContext> : XFormControlBase, ICoreControl {
		public IXForm XForm { get; set; }
		public IContainerControl<TContext> Parent { get; set; }
		public object Value { get; set; }
		public string Help { get; set; }
		public string Hint { get; set; }
		public string Alert { get; set; }
		public Appearance Appearance { get; set; }
		public int NavIndex { get; set; }
		public char AccessKey { get; set; }
		public bool ReadOnly { get; set; }
		public bool Required { get; set; }
		public string Calculate { get; set; }
		public string Constraint { get; set; }

		protected CoreControlBase(IContainerControl<TContext> parent, string id)
			: this(parent, id, null) {
		    if (id == null)
		        throw new ArgumentNullException("ID cannot be null");
		}

		protected CoreControlBase(IContainerControl<TContext> parent, Expression bind)
			: this(parent, null, bind) {
			if (bind == null)
				throw new ArgumentNullException("Bind cannot be null");
		}

		private CoreControlBase(IContainerControl<TContext> parent, string id, Expression bind)
			: base(parent, id, bind) {
			XForm = parent.XForm;
			Parent = parent;

			if (parent.SetChildAsInRepeatTemplate)
				Parent.Controls.Add(this);
		}

		public virtual void ValidateAgainstModelState() {
			if (XForm.ModelState.ContainsKey(FullID) && XForm.ModelState[FullID].Errors.Any()) {
				CssClass.Add("invalid");
				Alert = XForm.ModelState[FullID].Errors.Last().ErrorMessage;
			}
		}

		public virtual void SetValueFromRequest() {
			if (XForm.ModelState[FullID] != null && XForm.ModelState[FullID].Value != null) {
				Value = XForm.ModelState[FullID].Value.AttemptedValue;
			} else if (XForm.FormCollection[FullID] != null) {
				Value = XForm.FormCollection[FullID];
			}
		}

		public abstract string Render();
		protected abstract Func<object> SetValueAction { get; }

		public string ValidateThenRender() {
			if (!ReadOnly) {
				ValidateAgainstModelState();
				SetValueFromRequest();
			}
			SetLabel();
			SetValue();
			return Render();
		}

		public virtual void SetValue() {
			if (Value == null) {
				try {
					Value = SetValueAction();
				}
				catch {
					Value = "";
				}
			}
		}

		public override object ToJson() {
			if (Actions.Count > 0) {
				var json = new {
					id = ID,
					type = (int)ControlType,
					acts = Actions.Select(a => a.ToJson())
				};
				return json;
			}

			return null;
		}
	}
}
